package advworld.util;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents a node of the Tree<T> class. The Node<T> is also a container, and
 * can be thought of as instrumentation to determine the location of the type T
 * in the Tree<T>.
 */
public class Node<T> {
 
    public T data;
    public List<Node<T>> children;
    private boolean isChildrenNull;
 
    /**
     * Default ctor.
     */
    public Node() {
        children = null;
        data = null;
        isChildrenNull = true;
    }
 
    /**
     * Convenience ctor to create a Node<T> with an instance of T.
     * @param data an instance of T.
     */
    public Node(T data) {
    	children = null;
        this.data = data;
        isChildrenNull = true;
    }
     
    /**
     * Return the children of Node<T>. The Tree<T> is represented by a single
     * root Node<T> whose children are represented by a List<Node<T>>. Each of
     * these Node<T> elements in the List can have children. The getChildren()
     * method will return the children of a Node<T>.
     * @return the children of Node<T>
     */
    public List<Node<T>> getChildren() {
    	if (isChildrenNull) {
    		isChildrenNull = false;
    		return new ArrayList<Node<T>>();
    	}
        return children;
    }
 
    /**
     * Sets the children of a Node<T> object. See docs for getChildren() for
     * more information.
     * @param children the List<Node<T>> to set.
     */
    public void setChildren(List<Node<T>> children) {
        this.children = children;
    }
 
    /**
     * Returns the number of immediate children of this Node<T>.
     * @return the number of immediate children.
     */
    public int getNumberOfChildren() {
    	if (isChildrenNull) {
    		isChildrenNull = false;
    		return 0;
    	}
        return children.size();
    }
     
    /**
     * Adds a child to the list of children for this Node<T>.
     * @param child a Node<T> object to set.
     */
    public boolean addChild(Node<T> child) {
    	if (isChildrenNull) {
    		isChildrenNull = false;
    		children = new ArrayList<Node<T>>();
    	}
        return children.add(child);
    }
     
    /**
     * Inserts a Node<T> at the specified position in the child list. Will     
     * throw an ArrayIndexOutOfBoundsException if the index does not exist.
     * @param index the position to insert at.
     * @param child the Node<T> object to insert.
     * @throws IndexOutOfBoundsException if thrown.
     */
    public void insertChildAt(int index, Node<T> child) throws IndexOutOfBoundsException {
    	if (isChildrenNull) {
    		isChildrenNull = false;
    		children = new ArrayList<Node<T>>();
    	}
    	children.add(index, child);
    }
     
    /**
     * Remove the Node<T> element at index index of the List<Node<T>>.
     * @param index the index of the element to delete.
     * @throws IndexOutOfBoundsException if thrown.
     */
    public void removeChildAt(int index) throws IndexOutOfBoundsException {
    	try {
    		children.remove(index);
    	} catch (NullPointerException e) {
    		throw new IndexOutOfBoundsException();
    	}
        
    }
 
    public T getData() {
        return data;
    }
 
    public void setData(T data) {
        this.data = data;
    }
     
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{").append(getData().toString()).append(",[");
        int i = 0;
        for (Node<T> e : getChildren()) {
            if (i > 0) {
                sb.append(",");
            }
            sb.append(e.getData().toString());
            i++;
        }
        sb.append("]").append("}");
        return sb.toString();
    }
}